
library ieee;
	use ieee.std_logic_1164.all;
 	use ieee.std_logic_unsigned.all;
 	use ieee.std_logic_arith.all;
 	use ieee.math_real.all; 
 	use ieee.numeric_std.all;
 
library work;
    use work.router_pack.all;
    use work.env_pack.all;
    
package env_functions is

-------------------------------------------------------------------------
function calc_uniform   (randNum:real; max_num:integer; min_num:integer) return integer;
function calc_uniform   (randNum:real; width:integer) return integer;

function calc_wait_time (randNum:real; 
                         workload:std_logic_vector(e_workload_width_c-1 downto 0);                          
                         prev_wait_time:std_logic_vector(inj_wait_max_width_c-1 downto 0)) 
                         return std_logic_vector;

function calc_packet_route(local_x:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           local_y:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           dest_x:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           dest_y:std_logic_vector(e_coord_position_width_c-1 downto 0))
                           return std_logic_vector;

end env_functions;

-------------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;

package body env_functions is

function calc_uniform   (randNum:real; width:integer) return integer is
variable return_val : real;

begin	
	return_val := randNum * real(2**width);
    	return_val := floor(return_val);
    	return integer(return_val);
end calc_uniform;

-------------------------------------------------------------------------

function calc_uniform   (randNum:real; 
                         max_num:integer; 
                         min_num:integer) return integer is
variable temp_val  : real;

begin
	temp_val := real(min_num) + randNum * real(max_num + 1 - min_num);
    	temp_val := floor(temp_val);
    	return integer(temp_val);
end calc_uniform;

-------------------------------------------------------------------------

function calc_wait_time (randNum:real; 
                         workload:std_logic_vector(e_workload_width_c-1 downto 0);                         
                         prev_wait_time:std_logic_vector(inj_wait_max_width_c-1 downto 0)) 
                         return std_logic_vector is
variable temp_rand_num  : integer;  
variable mean_wait_time : real;
variable alpha : real;
variable wait_time_vec : std_logic_vector(inj_wait_max_width_c-1 downto 0);                       

begin
	alpha := (real(10) / (real(conv_integer(workload)))) - real(1);    
	mean_wait_time := real(conv_integer(prev_wait_time)) * alpha;
	temp_rand_num := calc_uniform(randNum, integer(real(2) * mean_wait_time), 0);
	wait_time_vec := conv_std_logic_vector(temp_rand_num, inj_wait_max_width_c);
	return wait_time_vec;
end calc_wait_time;

-------------------------------------------------------------------------

function calc_packet_route(local_x:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           local_y:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           dest_x:std_logic_vector(e_coord_position_width_c-1 downto 0);
                           dest_y:std_logic_vector(e_coord_position_width_c-1 downto 0))
                           return std_logic_vector is
variable total_x_steps : integer;
variable total_y_steps : integer;
variable temp_x_steps	: integer;
variable temp_y_steps : integer;
variable temp_tout : std_logic_vector(e_flit_data_width_c-1 downto 0);
variable step : integer;

begin
   total_x_steps := conv_integer(dest_x) - conv_integer(local_x);
   total_y_steps := conv_integer(dest_y) - conv_integer(local_y);
   temp_x_steps := total_x_steps;
   temp_y_steps := total_y_steps;
   step := e_flit_data_width_c-1;
   
   -- go right
   while (temp_y_steps > 0) loop
   	-- start going right from a Module
   	if (temp_y_steps = total_y_steps) then
  		temp_tout(step downto step-1) := "11";
	-- keep going right
  	else
  		temp_tout(step downto step-1) := "10";
   	end if;
   	step := step - 2;
   	temp_y_steps := temp_y_steps - 1;
   end loop;
   
   -- go left
   while (temp_y_steps < 0) loop
   	-- start going left from a Module
   	if(temp_y_steps = total_y_steps) then
   		temp_tout(step downto step-1) := "01";
   	-- keep going left
   	else
   		temp_tout(step downto step-1) := "01";
   	end if;
	step := step - 2;
   	temp_y_steps := temp_y_steps + 1;
   end loop;
   
   -- go down
   while (temp_x_steps > 0) loop
   	-- if starting to go down
   	if(temp_x_steps = total_x_steps) then
   		-- if came from the left
   		if(total_y_steps > 0) then
   			temp_tout(step downto step-1) := "00";
		-- if came from the right
   		elsif(total_y_steps < 0) then
   			temp_tout(step downto step-1) := "00";
   		-- if came from the Module
   		else
   			temp_tout(step downto step-1) := "00";
   		end if;
   	-- if continuing to go down
   	else
   		temp_tout(step downto step-1) := "00";
      	end if;
      	step := step - 2;
   	temp_x_steps := temp_x_steps - 1;
   end loop;
   
   -- go up
   while (temp_x_steps < 0) loop
	-- if starting to go up
   	if(temp_x_steps = total_x_steps) then
   		-- if came from the left
   		if(total_y_steps > 0) then
   			temp_tout(step downto step-1) := "01";
		-- if came from the right
   		elsif(total_y_steps < 0) then
   			temp_tout(step downto step-1) := "10";
   		-- if came from the Module
   		else
   			temp_tout(step downto step-1) := "10";
   		end if;
   	-- if continuing to go up
   	else
   		temp_tout(step downto step-1) := "01";
      	end if;
      	step := step - 2;
   	temp_x_steps := temp_x_steps + 1;
   end loop;
   
   -- Enrty to the destination Module
   if((temp_x_steps = 0) and (temp_y_steps = 0)) then
   	temp_tout(step downto step-1) := "11";
   	step := step - 2;
   	
	while (step >= 1) loop		-- while there are unassigned spaces left in the routing path
   		temp_tout(step downto step-1) := "00";
   		step := step - 2;
   	end loop;
   	
   end if;
   
   return temp_tout;
   
end calc_packet_route;

end env_functions;